/*
 * Copyright (C) 2022, KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

#include "sidebar-window-helper.h"
#include "screen-monitor.h"
//#include "layout-config.h"
#include "hand-gesture-helper.h"
#include "global-settings.h"

// kysdk
#include <kysdk/applications/waylandhelper.h>
#include <kysdk/applications/windowmanager/windowmanager.h>

// 窗管特效接口
#include <KWindowEffects>
#include <KWindowSystem>

// Qt
#include <QQuickItem>
#include <QX11Info>
#include <QMouseEvent>
#include <QTouchEvent>

#define HAND_GESTURE_WIDTH 2

namespace Sidebar {

static SidebarWindowHelper *globalWindowHelperInstance = nullptr;

void SidebarWindowDefineModule::defineModules(const char *uri, int versionMajor, int versionMinor)
{
    qRegisterMetaType<Sidebar::SidebarWindowType::Type>("SidebarWindowType::Type");
    qmlRegisterUncreatableType<Sidebar::SidebarWindowType>(uri, versionMajor, versionMinor, "SidebarWindowType", "Enum");

    qmlRegisterRevision<QWindow, 2>(uri, versionMajor, versionMinor);
    qmlRegisterRevision<QQuickWindow, 2>(uri, versionMajor, versionMinor);

    qmlRegisterType<Sidebar::StatusBarWindow>(uri, versionMajor, versionMinor, "StatusBarWindow");
    qmlRegisterType<Sidebar::RightHandGestureWindow>(uri, versionMajor, versionMinor, "RightHandGestureWindow");
    qmlRegisterType<Sidebar::NotificationCenterWindow>(uri, versionMajor, versionMinor, "NotificationCenterWindow");
}

SidebarWindowHelper *SidebarWindowHelper::instance(QObject *parent)
{
    if (!globalWindowHelperInstance) {
        globalWindowHelperInstance = new SidebarWindowHelper(parent);
    }

    return globalWindowHelperInstance;
}

SidebarWindowHelper::SidebarWindowHelper(QObject *parent) : QObject(parent)
{
    updateRects();

    ScreenMonitor *screenMonitor = ScreenMonitor::getInstance();
    connect(screenMonitor, &ScreenMonitor::geometryChanged, this, &SidebarWindowHelper::updateRects);
    connect(screenMonitor, &ScreenMonitor::primaryScreenChanged, this, &SidebarWindowHelper::updateRects);
}

void SidebarWindowHelper::setWindowGeometry(QWindow *window, const QRect& rect)
{
    if (!window) {
        qWarning() << "SidebarWindowHelper::updateWindowGeometry target window is null.";
        return;
    }

    window->setGeometry(rect);
    kdk::WindowManager::setGeometry(window, rect);
}

void SidebarWindowHelper::setWindowFlags(QWindow *window, SidebarWindowType::Type type)
{
    if (!window) {
        return;
    }

    switch (type) {
        default:
        case SidebarWindowType::Normal:
            break;
        case SidebarWindowType::Sidebar:
        case SidebarWindowType::StatusBar:
        case SidebarWindowType::RightHandGesture:
        case SidebarWindowType::NotificationCenter: {
            window->setFlags(Qt::FramelessWindowHint);
            break;
        }
    }
}

void SidebarWindowHelper::setWindowAttribute(QWindow *window, SidebarWindowType::Type type)
{
    // TODO 为每个窗口设置不同的属性（按需求）
    if (!window) {
        return;
    }

    switch (type) {
        default:
        case SidebarWindowType::Normal:
            return;
        case SidebarWindowType::Sidebar:
        case SidebarWindowType::StatusBar:
        case SidebarWindowType::RightHandGesture:
        case SidebarWindowType::NotificationCenter: {
            // 设置窗口类型
            KWindowSystem::setType(window->winId(), NET::SystemWindow);
            break;
        }
    }

    // 窗口管理器属性 设置跳过多任务视图，设置跳过任务栏
    kdk::WindowManager::setSkipTaskBar(window, true);
    kdk::WindowManager::setSkipSwitcher(window, true);
}

QRect SidebarWindowHelper::getWindowGeometry(SidebarWindowType::Type type)
{
    switch (type) {
        case SidebarWindowType::NotificationCenter:
            return m_notificationCenterRect;
        case SidebarWindowType::StatusBar:
            return m_statusBarRect;
        case SidebarWindowType::RightHandGesture:
            return m_rightGestureRect;
        default:
            return {};
    }
}

void SidebarWindowHelper::updateWindowGeometry(QWindow *window, SidebarWindowType::Type type)
{
    switch (type) {
        case SidebarWindowType::NotificationCenter:
            SidebarWindowHelper::setWindowGeometry(window, m_notificationCenterRect);
            break;
        case SidebarWindowType::StatusBar:
            SidebarWindowHelper::setWindowGeometry(window, m_statusBarRect);
            break;
        case SidebarWindowType::RightHandGesture:
            SidebarWindowHelper::setWindowGeometry(window, m_rightGestureRect);
            break;
        default:
            break;
    }
}

/**
 * 根据不同的窗口类型与主屏幕参数设置每一个窗口的geometry属性
 */
void SidebarWindowHelper::updateRects()
{
    QRect primaryScreenRect = ScreenMonitor::getInstance()->getGeometry();
    m_notificationCenterRect = primaryScreenRect;

    m_statusBarRect.setRect(primaryScreenRect.x(), primaryScreenRect.y(), primaryScreenRect.width(), HAND_GESTURE_WIDTH);

    m_rightGestureRect.setRect(primaryScreenRect.x() + primaryScreenRect.width() - HAND_GESTURE_WIDTH,
                               primaryScreenRect.y() + HAND_GESTURE_WIDTH,
                               HAND_GESTURE_WIDTH,
                               primaryScreenRect.height() - HAND_GESTURE_WIDTH);

    Q_EMIT geometryChanged();
}

/**
 * 所有窗口的基类，用于保证窗口的geometry始终与SidebarWindowHelper提供的参数一致
 * 屏蔽move事件，防止窗口被移动
 * @param parent
 */
SidebarWindowBase::SidebarWindowBase(QWindow *parent)
    : SidebarWindowBase(SidebarWindowType::Normal, parent)
{

}

/**
 * 每一个窗口都必须指定一个type所允许的类型
 * @param type 默认type为Normal，该类型不会触发任何动作规则
 * @param parent
 */
SidebarWindowBase::SidebarWindowBase(SidebarWindowType::Type type, QWindow *parent)
    : QQuickWindow(parent), m_windowType(type)
{
    initBase();
}

void SidebarWindowBase::initBase()
{
    if (m_windowType != SidebarWindowType::Normal) {
        SidebarWindowHelper::setWindowFlags(this, m_windowType);
        //SidebarWindowHelper::setWindowAttribute(this, m_windowType);

        if (m_windowType != SidebarWindowType::Sidebar) {
            updateGeometry();
            connect(SidebarWindowHelper::instance(), &SidebarWindowHelper::geometryChanged,
                    this, &SidebarWindowBase::updateGeometry);
        }
    }
}

bool SidebarWindowBase::event(QEvent *event)
{
    if (m_windowType != SidebarWindowType::Normal) {
        if (event->type() == QEvent::Move) {
            //防止窗口被挪动，可能是窗管或者wayland环境引起的pos改变
            updateGeometry();
            return true;
        }
    }

    return QQuickWindow::event(event);
}

SidebarWindowType::Type SidebarWindowBase::windowType()
{
    return m_windowType;
}

void SidebarWindowBase::updateGeometry()
{
    SidebarWindowHelper::setWindowGeometry(this, windowGeometry());
    QQuickItem *item = contentItem();
    if (item && item->size() != size()) {
        item->setSize(size());
    }
}

void SidebarWindowBase::exposeEvent(QExposeEvent *event)
{
    if (!QX11Info::isPlatformX11())
        SidebarWindowHelper::setWindowAttribute(this, m_windowType);
    updateGeometry();
    QQuickWindow::exposeEvent(event);
}

QRect SidebarWindowBase::windowGeometry()
{
    return SidebarWindowHelper::instance()->getWindowGeometry(m_windowType);
}

StatusBarWindow::StatusBarWindow(QWindow *parent)
    : SidebarWindowBase(SidebarWindowType::StatusBar, parent)
{
    setColor("transparent");
    SidebarWindowHelper::setWindowAttribute(this, SidebarWindowType::StatusBar);

    setVisible(isTabletModel());
    connect(Sidebar::GlobalSettings::globalInstance(), &Sidebar::GlobalSettings::valueChanged, this, [this] (const QString &key) {
        if (key == TABLET_MODE) {
            setVisible(isTabletModel());
            Q_EMIT isTabletModelChanged();
        }
    });
}

bool StatusBarWindow::isTabletModel() const
{
    return Sidebar::GlobalSettings::globalInstance()->getValue(TABLET_MODE).toBool();
}

RightHandGestureWindow::RightHandGestureWindow(QWindow *parent)
    : SidebarWindowBase(SidebarWindowType::RightHandGesture, parent)
{
    setColor("transparent");
    SidebarWindowHelper::setWindowAttribute(this, SidebarWindowType::RightHandGesture);

    connect(GlobalSettings::globalInstance(), &GlobalSettings::valueChanged, this, [this] (const QString &key) {
        if (key == TABLET_MODE && m_isPressed) {
            m_isPressed = false;
        }
    });
}

bool RightHandGestureWindow::event(QEvent *event)
{
    switch (event->type()) {
        case QEvent::TouchBegin: {
            auto *touchEvent = static_cast<QTouchEvent*>(event);

            if (touchEvent->window() == this) {
                m_isPressed = true;
                return true;
            }

            break;
        }
        case QEvent::TouchUpdate: {
            auto *touchEvent = static_cast<QTouchEvent*>(event);
            if (touchEvent->window() == this && m_isPressed) {
                int touchX = touchEvent->touchPoints().first().pos().x();
                callControlCenter(touchX);
                return true;
            }

            break;
        }
        case QEvent::TouchEnd: {
            auto *touchEvent = static_cast<QTouchEvent*>(event);
            if (touchEvent->window() == this) {
                m_isPressed = false;
                int touchX = touchEvent->touchPoints().first().pos().x();
                int touchY = touchEvent->touchPoints().first().pos().y();
                callControlCenterEnd(touchX, touchY);
                return true;
            }

            break;
        }
        case QEvent::MouseButtonPress: {
            auto *mouseEvent = static_cast<QMouseEvent*>(event);
            if (mouseEvent->button() == Qt::LeftButton) {
                m_isPressed = true;
                return true;
            }

            break;
        }
        case QEvent::MouseButtonRelease: {
            auto *mouseEvent = static_cast<QMouseEvent*>(event);
            if (mouseEvent->button() == Qt::LeftButton) {
                m_isPressed = false;
                callControlCenterEnd(mouseEvent->x(), mouseEvent->y());
                return true;
            }

            break;
        }
        case QEvent::MouseMove: {
            if (m_isPressed) {
                callControlCenter(static_cast<QMouseEvent*>(event)->x());
                return true;
            }

            break;
        }
        default:
            break;
    }

    return SidebarWindowBase::event(event);
}

void RightHandGestureWindow::callControlCenter(int x)
{
    if (x > -12) {
        return;
    }

    int right = windowGeometry().x() + x + 12;
    HandGestureHelper::getInstance()->callControlCenter(right);
}

void RightHandGestureWindow::callControlCenterEnd(int x, int y)
{
    int posX = windowGeometry().x() + x + 12;
    int posY = windowGeometry().y() + y + 12;
    HandGestureHelper::getInstance()->right2LeftRelease(posX, posY);
}

NotificationCenterWindow::NotificationCenterWindow(QWindow *parent)
    : SidebarWindowBase(SidebarWindowType::NotificationCenter, parent)
{
    setColor("transparent");
    SidebarWindowHelper::setWindowAttribute(this, SidebarWindowType::NotificationCenter);
}

} // Sidebar
